package com.knowswift.common.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.knowswift.common.annotation.SerialCode;
import com.knowswift.common.exception.CustomFailureException;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.time.LocalDate;


public class EntityUtils {

    /**
     * 设置编号
     * 适用于为对象存入数据库之前设置编号类的属性。如 编号、流水号
     * {@code entity}的编号类型field必须加上注解{@code SerialCode}
     *
     * @param entity  对象
     * @param service @Service类
     * @param <T>     T
     * @param unique  是否唯一
     */
    @SneakyThrows
    public static <T> void setCode(T entity, ServiceImpl<?, T> service, boolean unique) {
        Field field = null;
        int length = 0;
        for (Field _field : entity.getClass().getDeclaredFields()) {
            SerialCode serialCode = _field.getAnnotation(SerialCode.class);
            if (serialCode != null) {
                field = _field;
                length = serialCode.length();
                break;
            }
        }
        if (field == null) {
            throw new CustomFailureException("缺少带有SerialCode注解的属性");
        }
        field.setAccessible(true);
        String name = field.getName().replaceAll("[A-Z]", "_$0").toLowerCase();
        Object o = field.get(entity);
        if (o != null && o.toString().length() > 0) {
            return;
        }
        String code = nextCode(length);
        if (!unique) {
            field.set(entity, code);
        }
        int count = service.count(new QueryWrapper<T>().last(String.format(" and %s= '%s'", name, code)));
        if (count == 0) {
            field.set(entity, code);
        }
    }

    @SneakyThrows
    public static <T> void setCode(T entity, ServiceImpl<?, T> service) {
        setCode(entity, service, true);
    }

    @SneakyThrows
    public static <T> void setCode(T entity, BaseMapper<T> mapper) {
        setCode(entity, mapper, true);
    }

    @SneakyThrows
    public static <T> void setCode(T entity, BaseMapper<T> mapper, boolean unique) {
        Field field = null;
        int length = 0;
        for (Field _field : entity.getClass().getDeclaredFields()) {
            SerialCode serialCode = _field.getAnnotation(SerialCode.class);
            if (serialCode != null) {
                field = _field;
                length = serialCode.length();
                break;
            }
        }
        if (field == null) {
            throw new CustomFailureException("缺少带有SerialCode注解的属性");
        }
        field.setAccessible(true);
        String name = field.getName().replaceAll("[A-Z]", "_$0").toLowerCase();
        Object o = field.get(entity);
        if (o != null && o.toString().length() > 0) {
            return;
        }
        String code = nextCode(length);
        if (!unique) {
            field.set(entity, code);
        }
        int count = mapper.selectCount(new QueryWrapper<T>().last(String.format(" and %s= '%s'", name, code)));
        if (count == 0) {
            field.set(entity, code);
        }
    }


    public static String nextCode(int length) {

        if (length < 6) {
            return GetUUIDUtils.getUUIDBits(length);
        }
        String l = String.valueOf(System.currentTimeMillis() / 1000);
        if (length < 12) {
            String s = l.substring(l.length() - length > 9 ? 9 : 6);
            return s + GetUUIDUtils.getUUIDBits(length - s.length());
        }
        String string = LocalDate.now().toString().replaceAll("-", "");
        String substring = l.length() > (length - string.length()) ? l.substring(l.length() - (length - string.length())) : l;
        StringBuilder code = new StringBuilder(string + substring);
        while (length - code.length() > 0) {
            code.append(GetUUIDUtils.getUUIDBits(length - code.length()));
        }
        return code.toString();
    }

    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
        String s = nextCode(6);
        System.out.println(System.currentTimeMillis());
        System.out.println(s);
    }
}
